package com.forum.framework.web.core.simple;

import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

import java.time.Instant;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * 模拟Redis
 *
 * @author zihan.ouyang
 */
public class SimpleRedisConfig {
    private final ConcurrentHashMap<String, ValueWithExpire<?>> map = new ConcurrentHashMap<>();
    private final ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();

    public SimpleRedisConfig() {
        threadPoolTaskScheduler.initialize();
    }

    public void set(String key, String value) {
        map.put(key, new ValueWithExpire<>(value, -1));
    }

    public void setEx(String key, String value, long expireTime, TimeUnit timeUnit) {
        long expireTimestamp = System.currentTimeMillis() + timeUnit.toMillis(expireTime);
        map.put(key, new ValueWithExpire<>(value, expireTimestamp));
        schedulerExpiration(key, expireTimestamp, timeUnit);
    }

    public <T> void setCacheObject(final String key, final T value, final long expireTime, final TimeUnit timeUnit) {
        long expireTimestamp = System.currentTimeMillis() + timeUnit.toMillis(expireTime);
        map.put(key, new ValueWithExpire<>(value, expireTimestamp));
        schedulerExpiration(key, expireTimestamp, timeUnit);
    }

    @SuppressWarnings("unchecked")
    public <T> T getCacheObject(final String key) {
        ValueWithExpire<?> valueWithExpire = map.get(key);
        if (valueWithExpire == null) {
            return null;
        }
        if (valueWithExpire.expireTimestamp > 0 && valueWithExpire.expireTimestamp < System.currentTimeMillis()) {
            map.remove(key);
            return null;
        }
        return (T) valueWithExpire.getValueObj();
    }

    public String get(String key) {
        ValueWithExpire<?> valueWithExpire = map.get(key);
        if (valueWithExpire == null) {
            return null;
        }
        if (valueWithExpire.expireTimestamp > 0 && valueWithExpire.expireTimestamp < System.currentTimeMillis()) {
            map.remove(key);
        }
        return valueWithExpire.value;
    }

    public void delete(String key) {
        map.remove(key);
    }

    private void schedulerExpiration(String key, long expireTimestamp, TimeUnit timeUnit) {
        Instant instant = Instant.ofEpochMilli(System.currentTimeMillis() + timeUnit.toMillis(expireTimestamp));
        ScheduledFuture<?> future = threadPoolTaskScheduler.schedule(() -> {
            map.remove(key);
        }, instant);
    }

    private static class ValueWithExpire<T> {
        private String value;
        private T valueObj;
        private final long expireTimestamp;

        public ValueWithExpire(String value, long expireTimestamp) {
            this.value = value;
            this.expireTimestamp = expireTimestamp;
        }

        public ValueWithExpire(T valueObj, long expireTimestamp) {
            this.valueObj = valueObj;
            this.expireTimestamp = expireTimestamp;
        }

        public String getValue() {
            return value;
        }

        public T getValueObj() {
            return valueObj;
        }

        public long getExpireTimestamp() {
            return expireTimestamp;
        }
    }
}
